﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml;
using System.Xml.Serialization;
using Alogient.Cameleon.Sdk.Items;
using Cos.Core.Exception;
using Cos.Core.Service;
using Cos.Module.CMS;
using Cos.Module.Configuration;
using Cos.Module.Media;
using Cos.Module.Media.Exceptions;

namespace Alogient.Cameleon.Sdk
{
    /// <summary>
    /// Contains static methods for Page search
    /// </summary>
    public sealed class CMServices
    {
        private CMServices() {}

        /// <summary>
        /// Tells if the website is the Live version or the Preview version
        /// </summary>
        public static bool IsLive
        {
            get 
            {
                if (HttpContext.Current == null)
                {
                    return true;
                }

                return !(HttpContext.Current.Request.Url.Host.ToLower().IndexOf("preview.") >= 0);
            }
        }

        /// <summary>
        /// Get page from cache, without database connection
        /// </summary>
        /// <param name="navID">NavID</param>
        /// <param name="cultureCode">CultureCode</param>
        /// <returns>Page object from cache</returns>
        public static CMPage GetCachedPage(int navID, string cultureCode)
        {
            return GetPage(navID, cultureCode);
        }

        public static IEnumerable<CultureInfo> GetAllCultures()
        {
            var cultureService = ServiceLocator.Get<ICultureService>();

            var cultures = cultureService.GetCultures();

            return cultures.Select(c => c.CultureInfo);
        }

        /// <summary>
        /// Verify if a page exists for current culture
        /// </summary>
        /// <param name="navID">NavID</param>
        /// <returns>True if the page exist, false otherwise</returns>
        public static bool PageExists(int navID)
        {
            if (navID == 0)
            {
                return true;
            }

            return PageExists(navID, NavigationController.GetCultureCode());
        }

        /// <summary>
        /// Verify if a page exists
        /// </summary>
        /// <param name="navID">NavID</param>
        /// <returns>True if the page exist, false otherwise</returns>
        public static bool PageExists(int navID, string cultureCode)
        {
            var cultureService = ServiceLocator.Get<ICultureService>();
            var pageService = ServiceLocator.Get<IPageService>();

            try
            {
                var page = pageService.GetPage(cultureService.GetCulture(cultureCode), navID);

                if (page != null && page.IsVisible(IsLive))
                {
                    return true;
                }
            }
            catch (PageNotFoundException)
            {
                return false;
            }

            return false;
        }

        /// <summary>
        /// Get current navigation page
        /// </summary>
        /// <returns>Page object for current ID and culture</returns>
        public static CMPage GetPage()
        {
            return GetPage(NavigationController.GetNavID(), NavigationController.GetCultureCode());
        }

        /// <summary>
        /// Get page from NavID and current culture
        /// </summary>
        /// <param name="navID">NavID</param>
        /// <returns>Page object using NavID and current culture</returns>
        public static CMPage GetPage(int navID)
        {
            return GetPage(navID, NavigationController.GetCultureCode());
        }

        /// <summary>
        /// Get page
        /// </summary>
        /// <param name="navID">NavID</param>
        /// <param name="cultureCode">CultureCode</param>
        /// <returns>Page object</returns>
        public static CMPage GetPage(int navID, string cultureCode)
        {
            var key = String.Format("page_{0}_{1}", navID, cultureCode);
            if (HttpContext.Current != null && HttpContext.Current.Items.Contains(key))
            {
                return (CMPage)HttpContext.Current.Items[key];
            }

            return new CMPage(navID, cultureCode);
        }

        internal static void AddCachedPage(int navID, string cultureCode, CMPage page)
        {
            var key = String.Format("page_{0}_{1}", navID, cultureCode);
            if (HttpContext.Current != null && !HttpContext.Current.Items.Contains(key))
            {
                HttpContext.Current.Items.Add(key, page);
            }
            else if (HttpContext.Current != null && HttpContext.Current.Items[key] == null)
            {
                HttpContext.Current.Items[key] = page;
            }
        }

        /// <summary>
        /// Serialize an object
        /// </summary>
        /// <param name="obj">Object to serialize</param>
        /// <returns>Serialized object</returns>
        public static XmlDocument Serialize(object obj)
        {
            XmlSerializer serializer = new XmlSerializer(obj.GetType());
            StringWriter strWriter = new StringWriter();
            XmlTextWriter xmlTxtWriter = new XmlTextWriter(strWriter);
            xmlTxtWriter.Formatting = Formatting.Indented;
            serializer.Serialize(strWriter, obj);

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(strWriter.ToString());
            return xmlDoc;
        }
        
        /// <summary>
        /// List page collection matching full text search on element values, using paging
        /// </summary>
        /// <param name="query">Query string to search</param>
        /// <param name="cultureCode">Culture Code</param>
        /// <param name="resultsPerPage">Results per page</param>
        /// <param name="pageNo">Current results page</param>
        /// <returns>Matching page collection</returns>
        /// <example>CMServices.FullTextSearch("News", "en", 4, 1)</example>
        [Obsolete("Don't use it", true)]
        public static CMPageCollection FullTextSearch(string query, string cultureCode = null, int resultsPerPage = 0, int pageNo = 0)
        {
            return null; 
        }
        
        /// <summary>
        /// List filtered and ordered page collection, using paging
        /// </summary>
        /// <param name="queryFilter">Filter query</param>
        /// <param name="orderBy">Order criteria (add ' DESC' for descending order)</param>
        /// <param name="cultureCode">Culture Code</param>
        /// <param name="resPerPage">Results per page</param>
        /// <param name="pageNo">Current results page</param>
        /// <returns>Filtered page collection ordered by specified criteria</returns>
        /// <example>CMServices.SearchFilteredPages("Template.TemplateName = 'News'", "Element.Author DESC", "en", 4, 1)</example>
        [Obsolete("Use LINQ instead", true)]
        public static CMPageCollection SearchFilteredPages(string queryFilter, string orderBy = null, string cultureCode = null, int resPerPage = 0, int pageNo = 0)
        {
            return null;
        }

        /// <summary>
        /// List filtered and ordered pages by template for current culture, using paging
        /// </summary>
        /// <param name="templateName">Template Name</param>
        /// <param name="queryFilter">Filter query</param>
        /// <param name="orderBy">Order criteria (add ' DESC' for descending order)</param>
        /// <param name="cultureCode">Culture Code</param>
        /// <param name="resPerPage">Results per page</param>
        /// <param name="pageNo">Current results page</param>
        /// <returns>Filtered page collection</returns>
        /// <example>CMServices.SearchPagesByTemplate("News", "[Date] > '2005/02/01'", "Element.Author DESC", "en", 4, 1)</example>
        [Obsolete("Use GetPagesByTemplate and LINQ", true)]
        public static CMPageCollection SearchPagesByTemplate(string templateName, string queryFilter, string orderBy = null, string cultureCode = null, int resPerPage = 0, int pageNo = 0)
        {
            return null;
        }

        /// <summary>
        /// List pages using the template
        /// </summary>
        /// <param name="templateName">The template name.</param>
        /// <returns>List of pages</returns>
        public static CMPageCollection GetPagesByTemplate(string templateName, string cultureCode)
        {
            var cultureService = ServiceLocator.Get<ICultureService>();
            var pageService = ServiceLocator.Get<IPageService>();

            var pages = pageService.GetTemplatePages(templateName, cultureService.GetCulture(cultureCode)).Where(p => p.IsVisible(IsLive));
            
            var collection = new CMPageCollection();

            collection.AddRange(pages.Select(p => new CMPage(p)));

            return collection;
        }

        /// <summary>
        /// Get XML breadcrumbs for given ID and current culture
        /// </summary>
        /// <param name="navID">NavID</param>
        /// <returns>XML breadcrumbs</returns>
        public static XmlDocument XmlBreadcrumbs(int navID)
        {
            return XmlBreadcrumbs(navID, NavigationController.GetCultureCode());
        }

        /// <summary>
        /// Get XML breadcrumbs
        /// </summary>
        /// <param name="navID">NavID</param>
        /// <param name="cultureCode">CultureCode</param>
        /// <returns>XML breadcrumbs</returns>
        public static XmlDocument XmlBreadcrumbs(int navID, string cultureCode)
        {
            XmlDocument xmlDoc = Serialize(new CMPage(navID, cultureCode).Breadcrumbs);
            AddRootAttribute(ref xmlDoc, "NavID", navID);
            AddRootAttribute(ref xmlDoc, "CultureCode", cultureCode);
            return xmlDoc;
        }

        /// <summary>
        /// Add a custom attribute to root element of a xml document
        /// </summary>
        /// <param name="xmlDoc">XML document</param>
        /// <param name="name">Attribute name</param>
        /// <param name="value">Attribute value</param>
        private static void AddRootAttribute(ref XmlDocument xmlDoc, string name, object value)
        {
            XmlAttribute xmlAttribute = xmlDoc.CreateAttribute(name);
            xmlAttribute.Value = value.ToString();
            if (xmlDoc.DocumentElement != null)
            {
                xmlDoc.DocumentElement.Attributes.Append(xmlAttribute);
            }
        }

        public static CMVideo GetVideo(int videoID, string cultureCode)
        {
            var mediaService = ServiceLocator.Get<IMediaService>();

            try
            {
                var video = mediaService.GetVideo(videoID, cultureCode, IsLive);

                if (video == null)
                {
                    return null;
                }

                return !video.IsVisible ? null : new CMVideo(video);
            } 
            catch (VideoNotFoundException)
            {
                return null;
            }
        }
    }
}